package com.jevons.muffin.bind.adapter.recycleviewadatper;

import android.content.Context;
import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ObservableList;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.RecyclerView;

import com.chad.library.adapter.base.BaseQuickAdapter;
import com.jevons.muffin.adapter.BaseDataBindingHolder;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * Copyright 2020 JxGIS
 *
 * @file BindingRecyclerViewAdapter
 * @auth linaisheng
 * Created on 2023/12/7.
 * Description：
 */
public class BindingRecyclerViewAdapter<T> extends BaseQuickAdapter<T, BaseDataBindingHolder> implements BindingCollectionAdapter<T> {

    private static final Object DATA_INVALIDATION = new Object();

    private ItemBinding<? super T> itemBinding;
    private WeakReferenceOnListChangedCallback<T> callback;

    // Currently attached recyclerview, we don't have to listen to notifications if null.
    @Nullable
    private LifecycleOwner lifecycleOwner;

    public BindingRecyclerViewAdapter() {
        super(new ArrayList<>());
    }


    protected void setLayoutResId(int resId) {
        mLayoutResId = resId;
    }

    @Override
    public void setItemBinding(@NonNull ItemBinding<? super T> itemBinding) {
        this.itemBinding = itemBinding;
    }

    @NonNull
    @Override
    public ItemBinding<? super T> getItemBinding() {
        if (itemBinding == null) {
            throw new NullPointerException("itemBinding == null");
        }
        return itemBinding;
    }


    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        if (getRecyclerView() == null && mData instanceof ObservableList) {
            callback = new WeakReferenceOnListChangedCallback<>(this, (ObservableList<T>) mData);
            ((ObservableList<T>) mData).addOnListChangedCallback(callback);
        }
        super.bindToRecyclerView(recyclerView);
    }


    @Override
    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        if (getRecyclerView() != null && mData instanceof ObservableList) {
            ((ObservableList<T>) mData).removeOnListChangedCallback(callback);
            callback = null;
        }
    }


    public void setLifecycleOwner(@Nullable LifecycleOwner lifecycleOwner) {
        this.lifecycleOwner = lifecycleOwner;
        if (getRecyclerView() != null) {
            for (int i = 0; i < getRecyclerView().getChildCount(); i++) {
                View child = getRecyclerView().getChildAt(i);
                ViewDataBinding binding = DataBindingUtil.getBinding(child);
                if (binding != null) {
                    binding.setLifecycleOwner(lifecycleOwner);
                }
            }
        }
    }

    @Override
    protected void convert(BaseDataBindingHolder helper, T item) {
        convertPayloads(helper, item, Collections.emptyList());
    }

    @Override
    protected void convertPayloads(BaseDataBindingHolder helper, T item, List<Object> payloads) {
        super.convertPayloads(helper, item, payloads);
        ViewDataBinding binding = helper.getDataBinding();
        if (isForDataBinding(payloads)) {
            binding.executePendingBindings();
        } else if (itemBinding != null) {
            tryGetLifecycleOwner();
            if (itemBinding.bind(binding, item)) {
                binding.executePendingBindings();
                if (lifecycleOwner != null) {
                    binding.setLifecycleOwner(lifecycleOwner);
                }
            }
        }
    }

    @Override
    public void setNewData(@Nullable @org.jetbrains.annotations.Nullable List<T> items) {
        if (mData == items) {
            return;
        }
        // If a recyclerview is listening, set up listeners. Otherwise wait until one is attached.
        // No need to make a sound if nobody is listening right?
        if (getRecyclerView() != null) {
            if (mData instanceof ObservableList) {
                ((ObservableList<T>) mData).removeOnListChangedCallback(callback);
                callback = null;
            }
            if (items instanceof ObservableList) {
                callback = new WeakReferenceOnListChangedCallback<>(this, (ObservableList<T>) items);
                ((ObservableList<T>) items).addOnListChangedCallback(callback);
            }
        }
        super.setNewData(items);
    }

    @Override
    protected BaseDataBindingHolder onCreateDefViewHolder(ViewGroup parent, int viewType) {
        if (itemBinding != null) {
            int layoutId = itemBinding.layoutRes();
            return createBaseViewHolder(parent, layoutId);
        } else {
            return super.onCreateDefViewHolder(parent, viewType);
        }
    }

    @Override
    protected int getDefItemViewType(int position) {
        if (itemBinding != null) {
            itemBinding.onItemBind(position, mData.get(position));
            return itemBinding.layoutRes();
        } else {
            return super.getDefItemViewType(position);
        }
    }

    private void tryGetLifecycleOwner() {
        if (lifecycleOwner == null || lifecycleOwner.getLifecycle()
                .getCurrentState() == Lifecycle.State.DESTROYED) {
            lifecycleOwner = findLifecycleOwner(getRecyclerView());
        }
    }

    private boolean isForDataBinding(List<Object> payloads) {
        if (payloads == null || payloads.size() == 0) {
            return false;
        }
        for (int i = 0; i < payloads.size(); i++) {
            Object obj = payloads.get(i);
            if (obj != DATA_INVALIDATION) {
                return false;
            }
        }
        return true;
    }


    private static class WeakReferenceOnListChangedCallback<T> extends ObservableList.OnListChangedCallback<ObservableList<T>> {
        final WeakReference<BindingRecyclerViewAdapter<T>> adapterRef;

        WeakReferenceOnListChangedCallback(BindingRecyclerViewAdapter<T> adapter, ObservableList<T> items) {
            this.adapterRef = AdapterReferenceCollector.createRef(adapter, items, this);
        }

        @Override
        public void onChanged(ObservableList sender) {
            BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            ensureChangeOnMainThread();
            adapter.notifyDataSetChanged();
        }

        @Override
        public void onItemRangeChanged(ObservableList sender, final int positionStart, final int itemCount) {
            BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            ensureChangeOnMainThread();
            adapter.notifyItemRangeChanged(positionStart, itemCount);
        }

        @Override
        public void onItemRangeInserted(ObservableList sender, final int positionStart, final int itemCount) {
            BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            ensureChangeOnMainThread();
            adapter.notifyItemRangeInserted(positionStart, itemCount);
        }

        @Override
        public void onItemRangeMoved(ObservableList sender, final int fromPosition, final int toPosition, final int itemCount) {
            BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            ensureChangeOnMainThread();
            for (int i = 0; i < itemCount; i++) {
                adapter.notifyItemMoved(fromPosition + i, toPosition + i);
            }
        }

        @Override
        public void onItemRangeRemoved(ObservableList sender, final int positionStart, final int itemCount) {
            BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            ensureChangeOnMainThread();
            adapter.notifyItemRangeRemoved(positionStart, itemCount);
        }
    }


    @Nullable
    @MainThread
    static LifecycleOwner findLifecycleOwner(View view) {
        ViewDataBinding binding = DataBindingUtil.findBinding(view);
        LifecycleOwner lifecycleOwner = null;
        if (binding != null) {
            lifecycleOwner = binding.getLifecycleOwner();
        }
        Context ctx = view.getContext();
        if (lifecycleOwner == null && ctx instanceof LifecycleOwner) {
            lifecycleOwner = (LifecycleOwner) ctx;
        }
        return lifecycleOwner;
    }

    static void ensureChangeOnMainThread() {
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            throw new IllegalStateException("You must only modify the ObservableList on the main thread.");
        }
    }


}